package cz.mff.curreco.tapestry.pages;

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Date;
import java.util.ArrayList;

import javax.imageio.ImageIO;

import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.StreamResponse;
import org.apache.tapestry5.annotations.OnEvent;
import org.apache.tapestry5.annotations.Persist;
import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.services.Response;
import org.apache.tapestry5.upload.services.UploadedFile;



import cz.mff.curreco.common.Constants;
import cz.mff.curreco.model.Circle;
import cz.mff.curreco.model.CircleResultItem;
import cz.mff.curreco.model.MoneyItem;
import cz.mff.curreco.model.SessionObject;
import cz.mff.curreco.services.IDataService;
import cz.mff.curreco.services.IImageService;
import cz.mff.curreco.tapestry.base.BasePage;
import cz.mff.curreco.utils.Convert;
import cz.mff.curreco.utils.ImageUtils;
import cz.mff.curreco.utils.NormalizePicture;


public class Index extends BasePage {
	
	@Persist
	@Property
	private UploadedFile file;
	
	@Persist
	@Property
	private MoneyItem result;
	
	@Persist
	@Property
	private MoneyItem testMoneyItem;
	
	
	
	@Inject
	private IImageService imageService;
	
	@Inject
	private IDataService dataService;
	
	@Inject
	private ComponentResources resources;
	
	void beginRender() {
		if (getSessionObject()==null) {
			setSessionObject(new SessionObject());
		}
	}
	
	Object onSuccess() throws IOException {
		if (file==null) {
			return null;
		}
		
		getSessionObject().setImage(Convert.streamToImage(file.getStream()));	
		
		
		if(getSessionObject().getDescriptors() == null)
		{
			dataService.parseXML(getSessionObject());			
		}

		onRecognize();
		onOriginal();
		return this;
	}
	
	
	// FILTERS FOR IMAGE ON PAGE ----------------------------------------------
	
	@SuppressWarnings("unused")
	private void onThreshold() throws IOException {
		BufferedImage changed = imageService.threshold(getSessionObject().getImage(), 100);
		getSessionObject().setImage(changed);
	}	
	
	@SuppressWarnings("unused")
	private void onTestingModeOn() throws IOException {
		onOriginal();
		getSessionObject().setTestingMode(true);
	}	
	
	@SuppressWarnings("unused")
	private void onTestingModeOff() throws IOException {
		onOriginal();
		getSessionObject().setTestingMode(false);
	}	
	
	@SuppressWarnings("unused")
	private void onEdgeDetection() throws IOException {
		BufferedImage changed = imageService.edgeDetection(getSessionObject().getImage());
		getSessionObject().setImage(changed);
	}
	
	@SuppressWarnings("unused")
	private void onCoinDetection() throws IOException {
		Circle coinCircle = new Circle(0, 0, 0);
		int width = getSessionObject().getImage().getWidth();
		double ratio = 100f/width; 
		System.out.println("RATIO:"+ratio);
		coinCircle = imageService.houghTransformation(getSessionObject().getImage(), (imageService.getRadius(getSessionObject().getImage(), ratio))/2);	
		
		BufferedImage img =getSessionObject().getImage();
		int x = coinCircle.getCenterX();
		int y = coinCircle.getCenterY();
		int d = coinCircle.getDiameter();
		img.setRGB(x,y, ImageUtils.createRGBColor(255, 0, 0));
		img.setRGB(x+1,y, ImageUtils.createRGBColor(255, 0, 0));
		img.setRGB(x-1,y, ImageUtils.createRGBColor(255, 0, 0));
		img.setRGB(x,y+1, ImageUtils.createRGBColor(255, 0, 0));
		img.setRGB(x,y-1, ImageUtils.createRGBColor(255, 0, 0));
		
		BufferedImage resized = ImageUtils.resizeImage(getSessionObject().getImage(), 150/(double)d);
		getSessionObject().setImage(resized);
		
		width = getSessionObject().getImage().getWidth();
		ratio = 100f/width;
		coinCircle = imageService.houghTransformation(getSessionObject().getImage(), (imageService.getRadius(getSessionObject().getImage(), ratio))/2);
		
		getSessionObject().setCoinPosition(coinCircle);

	}
	
	
	@SuppressWarnings("unused")
	private void onNoiseFilter() throws IOException {
		getSessionObject().setImage(imageService.noiseReduction(getSessionObject().getImage()));
	}
	
	@SuppressWarnings("unused")
	private void onOriginal() throws IOException {
		getSessionObject().setImage(Convert.streamToImage(file.getStream()));
		getSessionObject().setImage(ImageUtils.resizeImage(getSessionObject().getImage(), 1));
	}
	
	@SuppressWarnings("unused")
	private void onCircle()	throws IOException 
	{		
			Circle a = getSessionObject().getCoinPosition();
			if (a==null)
				return;
			CircleResultItem res = imageService.getCircles(a, 30, Convert.imageToStream(getSessionObject().getImage()),0);
			if(testMoneyItem == null)
			{			
			testMoneyItem = new MoneyItem("test moneyItem", 0, "TMI", "aaa", "aaa", "aaa");			
			}
			testMoneyItem.setCircularDescriptorByValue(res.getDescriptor());
			MoneyItem x = testMoneyItem.getMostResemble(getSessionObject().getDescriptors(), 2000);
	}
	
	@SuppressWarnings("unused")
	private void onFatCircle() throws IOException 
	{
		if(getSessionObject().getDescriptors() == null)
		{
			dataService.parseXML(getSessionObject());			
		}
		Circle a = getSessionObject().getCoinPosition();
		CircleResultItem res = imageService.getCircles(a, 10, Convert.imageToStream(getSessionObject().getImage()),3);

		if(testMoneyItem == null)
		{			
			testMoneyItem = new MoneyItem("noise reduced test moneyItem", 0, "TMI", "", "", "");
		}
		
		testMoneyItem.setFatCircularDescriptorByValue(res.getDescriptor());

	}
	
	@SuppressWarnings("unused")
	private void onNormalize()	throws IOException 
	{
		BufferedImage result = getSessionObject().getImage();
		getSessionObject().setImage(NormalizePicture.Normalize(result));
	}
	@SuppressWarnings("unused")
	private void onNormalize2()	throws IOException 
	{
		BufferedImage result = getSessionObject().getImage();
		getSessionObject().setImage(NormalizePicture.Normalize2(result));
	}
	@SuppressWarnings("unused")
	private void onLoadDescriptor() throws IOException 
	{
		File myFoo = new File("data.xml");
		FileOutputStream fooStream = new FileOutputStream(myFoo, false); 
		byte[] myBytes = ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?><coins></coins>").getBytes(); 
		fooStream.write(myBytes);
		fooStream.close();
		
		
		int numberOfNormalCircles = 30;
		int numberOfFatCircles = 10;
		int fattnessOfFatCircles = 3;
		
		ArrayList<String> paths = new ArrayList<String>();
		paths.add("CoinPictures/1KCFront.PNG");
		paths.add("CoinPictures/2KCFront.PNG");
		paths.add("CoinPictures/5KCFront.PNG");
		paths.add("CoinPictures/10KCFront.PNG");
		paths.add("CoinPictures/20KCFront.PNG");
		//paths.add("CoinPictures/50KCFront.JPG");
		
		ArrayList<MoneyItem> items = new ArrayList<MoneyItem>();
		items.add(new MoneyItem("Jedna koruna ceska", 1, "KC", "CR", "CoinPictures/1KCFront.PNG", "CoinPictures/1KCBack.JPG"));
		items.add(new MoneyItem("Dve koruny ceske", 2, "KC", "CR", "CoinPictures/2KCFront.PNG", "CoinPictures/2KCBack.JPG"));
		items.add(new MoneyItem("Pet korun ceskych", 5, "KC", "CR", "CoinPictures/5KCFront.PNG", "CoinPictures/5KCBack.JPG"));
		items.add(new MoneyItem("Deset korun ceskych", 10, "KC", "CR", "CoinPictures/10KCFront.PNG", "CoinPictures/10KCBack.JPG"));
		items.add(new MoneyItem("Dvacet korun ceskych", 20, "KC", "CR", "CoinPictures/20KCFront.PNG", "CoinPictures/20KCBack.JPG"));
		//items.add(new MoneyItem("Padesat korun ceskych", 50, "KC", "CR", "CoinPictures/50KCFront.JPG", "CoinPictures/50KCBack.JPG"));
		
		for (int i = 0; i < paths.size(); i++) 
		{
			File active = new File(paths.get(i));
			MoneyItem activeItem = items.get(i);
			Image image = ImageIO.read(active);
			BufferedImage cpimg=bufferImage(image,BufferedImage.TYPE_INT_RGB);
			getSessionObject().setImage(cpimg);
			System.out.println("Computing descriptor for: " + activeItem.getOfficialName());
			onCoinDetection();
			onEdgeDetection();
			onThreshold();
			onThreshold();
			onNoiseFilter();
			
			Circle a = getSessionObject().getCoinPosition();
			if (a==null)
				return;
			CircleResultItem res = imageService.getCircles(a, numberOfNormalCircles, Convert.imageToStream(getSessionObject().getImage()),0);			
			testMoneyItem = activeItem;		
			testMoneyItem.setCircularDescriptorByValue(res.getDescriptor());
			
			
			image = ImageIO.read(active);
			cpimg=bufferImage(image,BufferedImage.TYPE_INT_RGB);
			getSessionObject().setImage(cpimg);
			onCoinDetection();			
			onEdgeDetection();
			onThreshold();
			onThreshold();
			onNoiseFilter();
			
			Circle b = getSessionObject().getCoinPosition();
			CircleResultItem resb = imageService.getCircles(b, numberOfFatCircles, Convert.imageToStream(getSessionObject().getImage()),fattnessOfFatCircles);						
			testMoneyItem.setFatCircularDescriptorByValue(resb.getDescriptor());		
			dataService.addItemToXML(testMoneyItem);			
		}
		/**
		for (int i = 0; i < paths.size(); i++) 
		{
			File active = new File(paths.get(i));
			MoneyItem activeItem = items.get(i);
			activeItem.setOfficialName(activeItem.getOfficialName() + " (Noise reduced)");
			Image image = ImageIO.read(active);
			BufferedImage cpimg=bufferImage(image,BufferedImage.TYPE_INT_RGB);
			getSessionObject().setImage(cpimg);
			System.out.println("Computing descriptor for: " + activeItem.getOfficialName());
			onCoinDetection();
			onEdgeDetection();
			onThreshold();
			onThreshold();
			onNoiseFilter();
			
			Circle a = getSessionObject().getCoinPosition();
			if (a==null)
				return;
			CircleResultItem res = imageService.getCircles(a, numberOfNormalCircles, Convert.imageToStream(getSessionObject().getImage()),0);			
			testMoneyItem = activeItem;		
			testMoneyItem.setCircularDescriptorByValue(res.getDescriptor());
			MoneyItem x = testMoneyItem.getMostResemble(getSessionObject().getDescriptors(), 2000);
			
			image = ImageIO.read(active);
			cpimg=bufferImage(image,BufferedImage.TYPE_INT_RGB);
			getSessionObject().setImage(cpimg);
			onCoinDetection();
			onEdgeDetection();
			onThreshold();
			onThreshold();
			onNoiseFilter();
			
			Circle b = getSessionObject().getCoinPosition();
			CircleResultItem resb = imageService.getCircles(b, numberOfFatCircles, Convert.imageToStream(getSessionObject().getImage()),fattnessOfFatCircles);						
			testMoneyItem.setFatCircularDescriptorByValue(resb.getDescriptor());		
			dataService.addItemToXML(testMoneyItem);			
		}
		**/
		dataService.parseXML(getSessionObject());		
		
	}
	
	@SuppressWarnings("unused")
	private void onRecognize()	throws IOException 
	{
		int numberOfNormalCircles = 30;
		int numberOfFatCircles = 10;
		int fattnessOfFatCircles = 3;
		
		int globalSuccesCounter = 0;
		int globalFailCounter = 0;
		
		Image image = getSessionObject().getImage();
		onCoinDetection();
		onEdgeDetection();
		onThreshold();
		onThreshold();
		onNoiseFilter();
		Circle b = getSessionObject().getCoinPosition();
		if (b==null)
			return;
		CircleResultItem resb = imageService.getCircles(b, numberOfFatCircles, Convert.imageToStream(getSessionObject().getImage()),fattnessOfFatCircles);			
		testMoneyItem = new MoneyItem("", 0, "", "", "", "");           									
		testMoneyItem.setFatCircularDescriptorByValue(resb.getDescriptor());
		MoneyItem x = testMoneyItem.getMostResembleByFatCircles(getSessionObject().getDescriptors(), 20000);
		
		getSessionObject().setResult(x);
		
		File resultFileImg = new File(x.getPictureFrontPath());           			
		Image resultImageF = ImageIO.read(resultFileImg);
		BufferedImage cpimgF = bufferImage(resultImageF,BufferedImage.TYPE_INT_RGB);
		getSessionObject().setResultImageFront(cpimgF);
		
		resultFileImg = new File(x.getPictureBackPath());           			
		Image resultImageB = ImageIO.read(resultFileImg);
		BufferedImage cpimgB = bufferImage(resultImageB,BufferedImage.TYPE_INT_RGB);
		getSessionObject().setResultImageBack(cpimgB);
	}
	
	public void onBenchmark() throws IOException
	{			
		int numberOfNormalCircles = 30;
		int numberOfFatCircles = 10;
		int fattnessOfFatCircles = 3;
		
		int globalSuccesCounter = 0;
		int globalFailCounter = 0;
		
		
		File folder = new File("benchmarkPictures");		
		File[] listOfDirectories = folder.listFiles();
		
		File benchmarkFile = new File("benchmark.txt");
		FileOutputStream fooStream = new FileOutputStream(benchmarkFile, false); 
		byte[] myBytes = ("BENCHMARK FOR CURRECO" + '\n').getBytes(); 
		fooStream.write(myBytes);
		
		
		for (int i = 0; i < listOfDirectories.length; i++) {
            if (!listOfDirectories[i].isFile()) 
            {
            	String subdirName = listOfDirectories[i].getName();
            	int value = Integer.parseInt(subdirName.substring(0, 2));                
                System.out.println("Now computing coins with value of " + value);
                myBytes = ("Now computing coins with value of " + value + '\n').getBytes();
                fooStream.write(myBytes);
                
                File[] listOfFiles = listOfDirectories[i].listFiles();
                int localSuccessCounter = 0;
            	int localFailCounter = 0;
                
                
                
                for (int j = 0; j < listOfFiles.length; j++) 
                {
                	if (listOfFiles[j].isFile()) 
                    { 
                    	
                    	
                    	
                    	File active = listOfFiles[j];            			
            			Image image = ImageIO.read(active);
            			BufferedImage cpimg=bufferImage(image,BufferedImage.TYPE_INT_RGB);
            			getSessionObject().setImage(cpimg);
            			
            			System.out.println(active.getName());
                        myBytes = (active.getName()).getBytes();
                        fooStream.write(myBytes);
            			
            			onCoinDetection();
            			onEdgeDetection();
            			onThreshold();
            			onThreshold();
            			onNoiseFilter();
            			
            			Circle b = getSessionObject().getCoinPosition();
            			if (b==null)
            				return;
            			CircleResultItem resb = imageService.getCircles(b, numberOfFatCircles, Convert.imageToStream(getSessionObject().getImage()),fattnessOfFatCircles);			
            			testMoneyItem = new MoneyItem("", 0, "", "", "", "");           									
            			testMoneyItem.setFatCircularDescriptorByValue(resb.getDescriptor());
            			MoneyItem x = testMoneyItem.getMostResembleByFatCircles(getSessionObject().getDescriptors(), 20000);
            			
            			myBytes = (" best resembles to: " + x.getOfficialName()).getBytes();
                        fooStream.write(myBytes);
                        
                        if(x.getAmount() != value)
                        {
                        	myBytes = (" FAIL" + '\n').getBytes();
                            fooStream.write(myBytes);
                            localFailCounter ++;
                        }
                        else
                        {
                        	myBytes = (" SUCCESS" + '\n').getBytes();
                            fooStream.write(myBytes);
                            localSuccessCounter ++;
                        }                   
                    }
                }
                myBytes = (" SUCCEDED: " + localSuccessCounter + " FAILED: " + localFailCounter + '\n').getBytes();
                fooStream.write(myBytes);
                myBytes = ("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + '\n').getBytes(); 
                fooStream.write(myBytes);
                globalFailCounter += localFailCounter;
                globalSuccesCounter += localSuccessCounter;
            }
            
        }
		
		myBytes = ("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + '\n').getBytes(); 
        fooStream.write(myBytes);
        myBytes = ("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + '\n').getBytes(); 
        fooStream.write(myBytes);
        myBytes = (" GLOBAL SUCCEDED: " + globalSuccesCounter + " GLOBAL FAILED: " + globalFailCounter + '\n').getBytes();
        fooStream.write(myBytes);
		
		
		
		
		
		fooStream.close();
		
	}

	//-------------------------------------------------------------------------
	
	public static BufferedImage bufferImage(Image image, int type) 
	{
		BufferedImage bufferedImage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
		Graphics2D g = bufferedImage.createGraphics();
		g.drawImage(image, null, null);		
		return bufferedImage;
	}
	
	@OnEvent(value = "showResultImageFront")
	@SuppressWarnings("unused")
	private Object getResultImageFrontForPage() throws Exception {
		final StreamResponse response = new StreamResponse() {

	        public String getContentType() {
	           return "image/jpegOrPngOrGif";
	        }

	        public InputStream getStream() throws IOException {
	            return Convert.imageToStream(getSessionObject().getResultImageFront());
	        }

	        public void prepareResponse(Response response) {
	        	//nothing to do
	        }

	    };
	    return response;
	}
	
	@OnEvent(value = "showResultImageBack")
	@SuppressWarnings("unused")
	private Object getResultImageBackForPage() throws Exception {
		final StreamResponse response = new StreamResponse() {

	        public String getContentType() {
	           return "image/jpegOrPngOrGif";
	        }

	        public InputStream getStream() throws IOException {
	            return Convert.imageToStream(getSessionObject().getResultImageBack());
	        }

	        public void prepareResponse(Response response) {
	        	//nothing to do
	        }

	    };
	    return response;
	}
	
	@OnEvent(value = "showImage")
	@SuppressWarnings("unused")
	private Object getImageForPage() throws Exception {
		final StreamResponse response = new StreamResponse() {

	        public String getContentType() {
	           return "image/jpegOrPngOrGif";
	        }

	        public InputStream getStream() throws IOException {
	            return Convert.imageToStream(getSessionObject().getImage());
	        }

	        public void prepareResponse(Response response) {
	        	//nothing to do
	        }

	    };
	    return response;
	}
	
	public String getImgUrl() throws Exception {
	    return resources.createFormEventLink("showImage").toAbsoluteURI();
	}
	
	public String getResultImgFrontUrl() throws Exception {
	    return resources.createFormEventLink("showResultImageFront").toAbsoluteURI();
	}
	
	public String getResultImgBackUrl() throws Exception {
	    return resources.createFormEventLink("showResultImageBack").toAbsoluteURI();
	}
	
	public MoneyItem getResultItem() throws Exception {
	    return getSessionObject().getResult();
	}
	
	public boolean isFileUploaded() {
		if (file!=null) {
			return true;
		} else {
			return false;
		}		
	}

	public boolean isWideImage() {
		if (getSessionObject().getImage().getWidth() > getSessionObject().getImage().getHeight())
			return true;
		return false;
	}
	
	public boolean isResize() {
		if (getSessionObject().getImage().getWidth() > Constants.MAX_IMAGE_WIDTH || 
				getSessionObject().getImage().getHeight() > Constants.MAX_IMAGE_HEIGHT)
			return true;
		return false;
	}
	
	public boolean isDetected() {
		if (getSessionObject().getCoinPosition()!=null) {
			return true;
		}
		return false;
	}
	
	public boolean isTestingMode() {
		if (getSessionObject().isTestingMode()) {
			return true;
		}
		return false;
	}
	
	public String getCoinPosition() {
		if (getSessionObject().getCoinPosition()!=null) {
			return "X:"+getSessionObject().getCoinPosition().getCenterX()+", Y:"+getSessionObject().getCoinPosition().getCenterY()+
					", R:"+getSessionObject().getCoinPosition().getDiameter();
		}
		return "";
	}
	
	public int getMaxImgWidth() {
		return Constants.MAX_IMAGE_WIDTH;
	}
	
	public int getMaxImgHeight() {
		return Constants.MAX_IMAGE_HEIGHT;
	}
	
	public IImageService getImageService() {
		return imageService;
	}

	public void setImageService(IImageService imageService) {
		this.imageService = imageService;
	}
	
}
